﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text.RegularExpressions;
using regxExtensions;

namespace regx
{
    public enum BreakIntoLines {Yes, No}
    public class StreamRexer
    { //=======================================================================
        private Regex           _rx      = null;
        private string          _replace = null;
        private BreakIntoLines  _bil     = BreakIntoLines.No;

        public StreamRexer(string pattern, RegexOptions rxopts, BreakIntoLines bil) : this(pattern, rxopts, null, bil) {}
        public StreamRexer(string pattern, RegexOptions rxopts, string replacepattern, BreakIntoLines bil)
        { //-------------------------------------------------------------------
            _rx = new Regex(pattern, rxopts);
            _replace = replacepattern;
            _bil = bil;
        }

        [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")] // Not relevant in an application
        public static explicit operator Regex(StreamRexer rexer)
        { //-------------------------------------------------------------------
            return rexer._rx;
        }

        public void DebugOut()
        { //-------------------------------------------------------------------
            Console.WriteLine("Regex: " + _rx.ToString());
            if (_replace.IsNotEmpty()) Console.WriteLine("Replace pattern: " + _replace);
            if (_replace == null)      Console.WriteLine(@"Null replace pattern, removing matched text");
            Console.WriteLine("Regex options: " + _rx.Options.ToString());
            Console.WriteLine(_bil == BreakIntoLines.Yes ? "Matching each input source once per line" : "Matching each input source once as a block of text");
            Console.WriteLine();
        }

        private string ReplacedOrNull(string Line)
        { //-------------------------------------------------------------------
            if (_replace == null) return null;
            return _rx.Replace(Line, _replace);
        }
        
        private IEnumerable<LineMatches> MatchResultsAllInput(Stream stm)
        { //-------------------------------------------------------------------
            using (StreamReader sr = new StreamReader(stm))
            {
                string Text = sr.ReadToEnd();;
                yield return new LineMatches() 
                                        {
                                            LineNumber   = 0, 
                                            SourceLine   = Text, 
                                            Matches      = _rx.Matches(Text),
                                            ReplacedLine = ReplacedOrNull(Text)
                                        };
            }
        }

        private IEnumerable<LineMatches> MatchResultsByLine(Stream stm)
        { //-------------------------------------------------------------------
            using (StreamReader sr = new StreamReader(stm))
            {
                ulong  LineNumber = 0;
                string Line       = null;

                while (null != (Line = sr.ReadLine()))
                {
                    LineNumber += 1;
                    yield return new LineMatches() 
                                        {
                                            LineNumber   = LineNumber, 
                                            SourceLine   = Line, 
                                            Matches      = _rx.Matches(Line),
                                            ReplacedLine = ReplacedOrNull(Line)
                                        };
                }
            }
        }
        
        public IEnumerable<LineMatches> MatchResults(Stream stm)
        { //-------------------------------------------------------------------
            switch (_bil)
            {
                case BreakIntoLines.Yes:
                    foreach (var r in MatchResultsByLine(stm)) yield return r;
                    break;

                case BreakIntoLines.No:
                    foreach (var r in MatchResultsAllInput(stm)) yield return r;
                    break;
            }
        }

    }
}
